﻿using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using WPFCABShell.Shell.Interface.Services.StatusBar;
using WPFCABShell.Shell.Services.StatusBar;

namespace WPFCABShell.UnitTest.Shell.Services.StatusBar
{
	[TestFixture]
	public sealed class StatusBarServiceTest : UnitTest.Shared.UnitTest
	{
		private StatusBarService _statusBarService;
		private MessageEventArgs _primaryMessageEventArgs;
		private MessageEventArgs _secondaryMessageEventArgs;
		private MessageEventArgs _ternaryMessageEventArgs;
		private ProgressEventArgs _progressMessageEventArgs;

		protected override void SetUpCore()
		{
			base.SetUpCore();
			ClearEvents();
			_statusBarService = new StatusBarService();

			_statusBarService.MessageChanged += delegate(object sender, MessageEventArgs e)
			{
				_primaryMessageEventArgs = e;
			};

			_statusBarService.SecondaryMessageChanged += delegate(object sender, MessageEventArgs e)
			{
				_secondaryMessageEventArgs = e;
			};

			_statusBarService.TernaryMessageChanged += delegate(object sender, MessageEventArgs e)
			{
				_ternaryMessageEventArgs = e;
			};

			_statusBarService.ProgressChanged += delegate(object sender, ProgressEventArgs e)
			{
				_progressMessageEventArgs = e;
			};
		}

		[Test]
		public void BeginMessageOperation_ShouldBeginMessageOperationWithNormalPriority()
		{
			IMessageOperation messageOperation = _statusBarService.BeginMessageOperation();
			Assert.AreEqual(StatusPriority.Normal, messageOperation.Priority);
			Assert.IsNull(messageOperation.Text);
			Assert.IsNull(messageOperation.Image);
			Assert.IsNotNull(_primaryMessageEventArgs);
			
			ClearEvents();

			messageOperation.Text = "Test";
			Assert.IsNotNull(_primaryMessageEventArgs);
			Assert.AreEqual("Test", _primaryMessageEventArgs.Text);

			ClearEvents();

			messageOperation.Image = "Image";
			Assert.IsNotNull(_primaryMessageEventArgs);
			Assert.AreEqual("Image", _primaryMessageEventArgs.Image);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Enum value '100' is not defined for enumeration 'WPFCABShell.Shell.Interface.Services.StatusBar.StatusPriority'.\r\nParameter name: priority")]
		public void BeginMessageOperation_Priority_ShouldThrowIfInvalidPriority()
		{
			_statusBarService.BeginMessageOperation((StatusPriority)100);
		}

		[Test]
		public void BeginMessageOperation_Priority_ShouldBeginOperationAtSpecifiedPriority()
		{
			IMessageOperation messageOperation = _statusBarService.BeginMessageOperation(StatusPriority.High);
			Assert.AreEqual(StatusPriority.High, messageOperation.Priority);
			Assert.IsNull(messageOperation.Text);
			Assert.IsNull(messageOperation.Image);
			Assert.IsNotNull(_primaryMessageEventArgs);
		}

		[Test]
		public void BeginSecondaryMessageOperation_ShouldBeginMessageOperationWithNormalPriority()
		{
			IMessageOperation messageOperation = _statusBarService.BeginSecondaryMessageOperation();
			Assert.AreEqual(StatusPriority.Normal, messageOperation.Priority);
			Assert.IsNull(messageOperation.Text);
			Assert.IsNull(messageOperation.Image);
			Assert.IsNotNull(_secondaryMessageEventArgs);

			ClearEvents();

			messageOperation.Text = "Test";
			Assert.IsNotNull(_secondaryMessageEventArgs);
			Assert.AreEqual("Test", _secondaryMessageEventArgs.Text);

			ClearEvents();

			messageOperation.Image = "Image";
			Assert.IsNotNull(_secondaryMessageEventArgs);
			Assert.AreEqual("Image", _secondaryMessageEventArgs.Image);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Enum value '100' is not defined for enumeration 'WPFCABShell.Shell.Interface.Services.StatusBar.StatusPriority'.\r\nParameter name: priority")]
		public void BeginSecondaryMessageOperation_Priority_ShouldThrowIfInvalidPriority()
		{
			_statusBarService.BeginSecondaryMessageOperation((StatusPriority)100);
		}

		[Test]
		public void BeginSecondaryMessageOperation_Priority_ShouldBeginOperationAtSpecifiedPriority()
		{
			IMessageOperation messageOperation = _statusBarService.BeginSecondaryMessageOperation(StatusPriority.High);
			Assert.AreEqual(StatusPriority.High, messageOperation.Priority);
			Assert.IsNull(messageOperation.Text);
			Assert.IsNull(messageOperation.Image);
			Assert.IsNotNull(_secondaryMessageEventArgs);
		}

		[Test]
		public void BeginTernaryMessageOperation_ShouldBeginMessageOperationWithNormalPriority()
		{
			IMessageOperation messageOperation = _statusBarService.BeginTernaryMessageOperation();
			Assert.AreEqual(StatusPriority.Normal, messageOperation.Priority);
			Assert.IsNull(messageOperation.Text);
			Assert.IsNull(messageOperation.Image);
			Assert.IsNotNull(_ternaryMessageEventArgs);

			ClearEvents();

			messageOperation.Text = "Test";
			Assert.IsNotNull(_ternaryMessageEventArgs);
			Assert.AreEqual("Test", _ternaryMessageEventArgs.Text);

			ClearEvents();

			messageOperation.Image = "Image";
			Assert.IsNotNull(_ternaryMessageEventArgs);
			Assert.AreEqual("Image", _ternaryMessageEventArgs.Image);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Enum value '100' is not defined for enumeration 'WPFCABShell.Shell.Interface.Services.StatusBar.StatusPriority'.\r\nParameter name: priority")]
		public void BeginTernaryMessageOperation_Priority_ShouldThrowIfInvalidPriority()
		{
			_statusBarService.BeginTernaryMessageOperation((StatusPriority)100);
		}

		[Test]
		public void BeginTernaryMessageOperation_Priority_ShouldBeginOperationAtSpecifiedPriority()
		{
			IMessageOperation messageOperation = _statusBarService.BeginTernaryMessageOperation(StatusPriority.High);
			Assert.AreEqual(StatusPriority.High, messageOperation.Priority);
			Assert.IsNull(messageOperation.Text);
			Assert.IsNull(messageOperation.Image);
			Assert.IsNotNull(_ternaryMessageEventArgs);
		}

		[Test]
		public void BeginProgressOperation_ShouldBeginProgressOperationWithNormalPriority()
		{
			IProgressOperation progressOperation = _statusBarService.BeginProgressOperation();
			Assert.AreEqual(StatusPriority.Normal, progressOperation.Priority);
			Assert.IsNull(progressOperation.Text);
			Assert.IsFalse(progressOperation.IsIndeterminate);
			Assert.AreEqual(0d, progressOperation.Minimum);
			Assert.AreEqual(0d, progressOperation.Maximum);
			Assert.AreEqual(0d, progressOperation.Value);
			Assert.IsNotNull(_progressMessageEventArgs);

			ClearEvents();

			progressOperation.Text = "Test";
			Assert.IsNotNull(_progressMessageEventArgs);
			Assert.AreEqual("Test", _progressMessageEventArgs.Text);

			ClearEvents();

			progressOperation.IsIndeterminate = true;
			Assert.IsNotNull(_progressMessageEventArgs);
			Assert.IsTrue(_progressMessageEventArgs.IsIndeterminate);

			ClearEvents();

			progressOperation.Minimum = 1;
			Assert.IsNotNull(_progressMessageEventArgs);
			Assert.AreEqual(1d, _progressMessageEventArgs.Minimum);

			ClearEvents();

			progressOperation.Maximum = 2;
			Assert.IsNotNull(_progressMessageEventArgs);
			Assert.AreEqual(2d, _progressMessageEventArgs.Maximum);

			ClearEvents();

			progressOperation.Value = 1.5;
			Assert.IsNotNull(_progressMessageEventArgs);
			Assert.AreEqual(1.5d, _progressMessageEventArgs.Value);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Enum value '100' is not defined for enumeration 'WPFCABShell.Shell.Interface.Services.StatusBar.StatusPriority'.\r\nParameter name: priority")]
		public void BeginProgressMessageOperation_Priority_ShouldThrowIfInvalidPriority()
		{
			_statusBarService.BeginTernaryMessageOperation((StatusPriority)100);
		}

		[Test]
		public void BeginProgressMessageOperation_Priority_ShouldBeginOperationAtSpecifiedPriority()
		{
			IMessageOperation messageOperation = _statusBarService.BeginTernaryMessageOperation(StatusPriority.High);
			Assert.AreEqual(StatusPriority.High, messageOperation.Priority);
			Assert.IsNull(messageOperation.Text);
			Assert.IsNull(messageOperation.Image);
			Assert.IsNotNull(_ternaryMessageEventArgs);
		}

		[Test]
		public void BeginOperation_ShouldOrderByPriorityMajorAndAgeMinor()
		{
			IMessageOperation operation1 = null;
			IMessageOperation operation2 = null;
			IMessageOperation operation3 = null;
			IMessageOperation operation4 = null;
			IMessageOperation operation5 = null;
			IMessageOperation operation6 = null;

			operation1 = _statusBarService.BeginMessageOperation(StatusPriority.Normal);
			operation1.Text = "operation1";
			//no surprises here
			Assert.AreEqual("operation1", _primaryMessageEventArgs.Text);

			operation2 = _statusBarService.BeginMessageOperation(StatusPriority.Low);
			operation2.Text = "operation2";
			//lower priority
			Assert.AreEqual("operation1", _primaryMessageEventArgs.Text);

			operation3 = _statusBarService.BeginMessageOperation(StatusPriority.Normal);
			operation3.Text = "operation3";
			//same priority but this operation is newer
			Assert.AreEqual("operation3", _primaryMessageEventArgs.Text);

			operation4 = _statusBarService.BeginMessageOperation(StatusPriority.High);
			operation4.Text = "operation4";
			//higher priority
			Assert.AreEqual("operation4", _primaryMessageEventArgs.Text);

			operation5 = _statusBarService.BeginMessageOperation(StatusPriority.Normal);
			operation5.Text = "operation5";
			//lower priority, even though it's newer
			Assert.AreEqual("operation4", _primaryMessageEventArgs.Text);

			operation6 = _statusBarService.BeginMessageOperation(StatusPriority.High);
			operation6.Text = "operation6";
			//high priority, and new
			Assert.AreEqual("operation6", _primaryMessageEventArgs.Text);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void EndOperation_ShouldThrowIfNull()
		{
			_statusBarService.EndOperation(null);
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Unable to end the specified operation. Make sure it was created by this StatusBarService.")]
		public void EndOperation_ShouldThrowIfUnableToEndOperation()
		{
			IMessageOperation messageOperation = Mocks.DynamicMock<IMessageOperation>();
			Mocks.ReplayAll();
			_statusBarService.EndOperation(messageOperation);
		}

		[Test]
		public void EndOperation_ShouldCauseCorrectOperationToBubbleToTop()
		{
			IMessageOperation operation1 = _statusBarService.BeginMessageOperation(StatusPriority.Low);
			IMessageOperation operation2 = _statusBarService.BeginMessageOperation(StatusPriority.Low);
			IMessageOperation operation3 = _statusBarService.BeginMessageOperation(StatusPriority.Normal);
			IMessageOperation operation4 = _statusBarService.BeginMessageOperation(StatusPriority.Normal);
			IMessageOperation operation5 = _statusBarService.BeginMessageOperation(StatusPriority.High);
			IMessageOperation operation6 = _statusBarService.BeginMessageOperation(StatusPriority.High);
			operation1.Text = "operation1";
			operation2.Text = "operation2";
			operation3.Text = "operation3";
			operation4.Text = "operation4";
			operation5.Text = "operation5";
			operation6.Text = "operation6";

			Assert.AreEqual("operation6", _primaryMessageEventArgs.Text);

			operation6.Dispose();
			Assert.AreEqual("operation5", _primaryMessageEventArgs.Text);

			operation4.Dispose();
			Assert.AreEqual("operation5", _primaryMessageEventArgs.Text);

			operation5.Dispose();
			Assert.AreEqual("operation3", _primaryMessageEventArgs.Text);

			operation1.Dispose();
			Assert.AreEqual("operation3", _primaryMessageEventArgs.Text);

			operation3.Dispose();
			Assert.AreEqual("operation2", _primaryMessageEventArgs.Text);

			operation2.Dispose();
			Assert.IsNull(_primaryMessageEventArgs.Text);
		}

		#region Helper Methods

		private void ClearEvents()
		{
			_primaryMessageEventArgs = null;
			_secondaryMessageEventArgs = null;
			_ternaryMessageEventArgs = null;
		}

		#endregion
	}
}
